﻿using System;
using System.Collections;
using OSC.NET;
using System.Drawing;
using System.Collections.Generic;

namespace Qurihara.OSCCom
{
	/// <summary>
	/// ClientList の概要の説明です。
	/// </summary>
	public class ClientList: ArrayList
	{
		public ClientList() :base()
		{
		}
		public void Send(string adr,string val)
		{
			foreach(Client cl in this)
			{
				cl.Send(adr,val);
			}
		}
        public void SendWithoutMe(Client me, string adr, string val)
        {
            foreach (Client cl in this)
            {
                if (!me.Equals(cl))
                    cl.Send(adr, val);
            }
        }
		public void Dispose()
		{
			foreach(Client cl in this)
			{
				cl.Dispose();
			}
		}

        public Client GetClientByName(string name)
        {
            foreach (Client cl in this)
            {
                if (cl.Name.Equals(name)) return cl;
            }
            return null;
        }
	}

    public abstract class Client// : OSCComCore
    {
        protected ClientList parentList;
        protected IComCore iComCore;
		public Client(IComCore iComCore, string ip,int port,string name,ClientList _parentList)
		{
            this.iComCore = iComCore;
            parentList = _parentList;
            iComCore.ConnectTransmitter(ip, port);
            iComCore.Name = name;
            iComCore.Initialize();
		}
        public string Name
        {
            get { return iComCore.Name; }
            set { iComCore.Name = value; }
        }
        public string Ip
        {
            get { return iComCore.Ip; }
        }
        public int MPort
        {
            get { return iComCore.MPort; }
        }

        public void Dispose()
        {
            iComCore.Dispose();
        }

        public void Send(string adr, string val)
        {
            iComCore.Send("provider", iComCore.Name, adr, val);
        }

        //void ConnectTransmitter(string serverIP, int serverPort);

    }

    public class ClientWithBitmapElement : Client
    {
        public enum RoleType
        {
            Presenter,
            Student,
            Screen
        }
        public static RoleType GetRoleTypeFromString(string src)
        {
            if (src.ToLower().Equals(RoleType.Presenter.ToString().ToLower()))
                return RoleType.Presenter;
            if (src.ToLower().Equals(RoleType.Student.ToString().ToLower()))
                return RoleType.Student;
            return RoleType.Screen;
        }

        private RoleType role;
        public Rectangle Rectangle; //clientが保持している、写すべき領域？不用意にbitmapElement.Boundingboxとはできなさそう。
        private BitmapElement bitmapElement;
        //public BitmapElement BitmapElement
        //{
        //    get { return bitmapElement; }
        //}

        protected Server server;
        //public ClientWithBitmapElement(Rectangle initRectangle, string ip, int port, string name,RoleType roleType,Server sv)//, ClientList list)
        public ClientWithBitmapElement(BitmapElement be,IComCore iComCore, string ip, int port, string name,RoleType roleType,Server sv)//, ClientList list)
            : base(iComCore, ip, port, name, sv.ClientList)//list)
        {
            //Rectangle = initRectangle;
            bitmapElement = be;
            Rectangle = be.BoundingBox; //ここで代入しているから変更されないことに注意。
            role = roleType;
            server = sv;

            iComCore.MessageReceived += new MessageEventHandler(iComCore_MessageReceived);
        }

        public bool InitRole()
        {
            try
            {
                Send("/provider",
                    Ip + "," + MPort.ToString() + "," + Name);
                System.Threading.Thread.Sleep(200);
                switch (role)
                {
                    case RoleType.Presenter:
                        EnableSendView(true);
                        System.Threading.Thread.Sleep(100);
                        EnableCanChangeAutoScroll(true);
                        System.Threading.Thread.Sleep(100);
                        EnableSendInkImmediately(true);
                        System.Threading.Thread.Sleep(100);
                        EnableSendToAllInk(true);
                        System.Threading.Thread.Sleep(100);
                        EnableSendPersonalInk(false);
                        System.Threading.Thread.Sleep(100);
                        EnableReceivePersonalInk(true);
                        break;
                    case RoleType.Student:
                        EnableSendView(false);
                        System.Threading.Thread.Sleep(100);
                        EnableCanChangeAutoScroll(true);
                        System.Threading.Thread.Sleep(100);
                        EnableSendInkImmediately(false);
                        System.Threading.Thread.Sleep(100);
                        EnableSendToAllInk(false);
                        System.Threading.Thread.Sleep(100);
                        EnableSendPersonalInk(true);
                        System.Threading.Thread.Sleep(100);
                        EnableReceivePersonalInk(false);
                        break;
                    case RoleType.Screen:
                    default:
                        EnableSendView(false);
                        System.Threading.Thread.Sleep(100);
                        EnableCanChangeAutoScroll(false);
                        System.Threading.Thread.Sleep(100);
                        EnableSendInkImmediately(true);
                        System.Threading.Thread.Sleep(100);
                        EnableSendToAllInk(true);
                        System.Threading.Thread.Sleep(100);
                        EnableSendPersonalInk(false);
                        System.Threading.Thread.Sleep(100);
                        EnableReceivePersonalInk(true);
                        break;
                }
            }
            catch
            {
                return false;
            }

            return true;
        }

        protected void EnableSendView(bool enable){
            Send("/enable__sendview", enable.ToString());
        }
        protected void EnableCanChangeAutoScroll(bool enable){
            Send("/enable_canchangeautoscroll", enable.ToString());
        }
        protected void EnableSendInkImmediately(bool enable){
            Send("/enable__sendinkimmediately", enable.ToString());
        }
        protected void EnableSendToAllInk(bool enable){
            Send("/enable__sendtoallink", enable.ToString());
        }
        protected void EnableSendPersonalInk(bool enable){
            Send("/enable_sendpersonalink", enable.ToString());
        }
        protected void EnableReceivePersonalInk(bool enable){
            Send("/enable__receivepersonalink", enable.ToString());
        }

        public void ShowPersonalInk(List<string> dispList)
        {
            Send("/hide__personal_strokes", bool.TrueString);
            foreach (string disp in dispList)
            {
                Send("/show__personal_strokes", disp);
            }
        }

        void iComCore_MessageReceived(object o, MessageEventArgs e)
        {
        //protected override void Parse(string adr, string data)
        //{
            if (!e.SenderName.Equals("@any") && !e.SenderName.Equals(iComCore.Name)) return;
            //if (!e.DestName.Equals("@any") && !e.DestName.Equals("provider")) return;
            //if (!e.SenderName.Equals("provider") && !e.DestName.Equals(iComCore.Name)) return;
            string adr = e.Adr;
            string data = e.Data;
            //if (adr.Equals(prefix + "/draw_strokes"))
            //{
            //    //string[] dat = data.Split(',');
            //    //CreateStrokes(dat[0]);
            //    parentList.SendWithoutMe(this, adr, data);
            //}
            //if (adr.Equals(prefix + "/private__strokes"))
            //{
            //    //string[] dat = data.Split(',');
            //    //CreateStrokes(dat[0]);
            //    parentList.SendWithoutMe(this, adr, data);
            //}
            //else 
            if (adr.Equals("/view_moved"))
            {
                //暫定的に、すべての画面を同じＶｉｅｗに同期させる。
                //parentList.SendWithoutMe(this, prefix + "/set_view",data);

                string[] dat = data.Split(',');
                Rectangle view = new Rectangle(
                    int.Parse(dat[0]), 
                    int.Parse(dat[1]), 
                    int.Parse(dat[2]), 
                    int.Parse(dat[3]));
                ArrangeView(view);

            }
            else
                if (adr.Equals("/go__snap"))
                {
                    string[] dat = data.Split(',');
                    int index = int.Parse(dat[0]);
                    ArrangeSnap(index);
                }
                else
            {
                parentList.SendWithoutMe(this, adr, data);
            }

        }

        #region arrangeview
        private List<GraphList> checkList = new List<GraphList>();

        private void ArrangeView(Rectangle view)
        {
            //1.自分のグループに関しては[自分以外相対位置表示]
            //2.equalgroupについて
            //3.まだ処理していないならば、自分と同じviewをそのグループの一番若い要素にセット。
            //4.再帰的に１を行う

            GraphList gl = server.IsContained(this);
            if (gl != null)
            {
                checkList.Clear();
                SendView(gl, view);
                checkList.Clear();
            }
        }
        private void SendView(GraphList gl, Rectangle r)
        {
            if (!checkList.Contains(gl))
            {
                checkList.Add(gl);
                SendViewInGroup(gl, r);
                foreach (GraphList egl in gl.Equal)
                {
                    SendView(egl, r);
                }
            }
        }
        private void SendViewInGroup(GraphList gl, Rectangle r)
        {
            //グループ内の位置関係をもとにＶｉｅｗを送る

            //initCをgroupの最初の要素としているところは暫定処置
            ClientWithBitmapElement initC = (ClientWithBitmapElement)gl[0];
            SendView2Client(initC, r);
            for (int i = 1; i < gl.Count; i++)
            {
                ClientWithBitmapElement cl = (ClientWithBitmapElement)gl[i];
                SendRelativeView2Client(initC, cl,r);
            }
        }
        private void SendView2Client(ClientWithBitmapElement c, Rectangle r)
        {
            string toSend =
                r.X.ToString() + "," +
                r.Y.ToString() + "," +
                r.Width.ToString() + "," +
                r.Height.ToString();
            c.Send("/set_view", toSend);
        }
        private void SendRelativeView2Client(ClientWithBitmapElement refC, ClientWithBitmapElement destC, Rectangle r)
        {
            Rectangle destR =
                ConvertRectangle(refC.bitmapElement.BoundingBox,r,destC.bitmapElement.BoundingBox);
                //ConvertRectangle(refC.bitmapElement.BoundingBox, destC.bitmapElement.BoundingBox, r);
            SendView2Client(destC, destR);
        }

        private static Rectangle ConvertRectangle(Rectangle r1, Rectangle R1, Rectangle r2)
        {
            int l1x = R1.X - r1.X;
            int l1y = R1.Y - r1.Y;
            float x2 = r2.X + (float)l1x / (float)r1.Width * r2.Width;
            float y2 = r2.Y + (float)l1y / (float)r1.Height * r2.Height;
            float w2 = (float)R1.Width / (float)r1.Width * r2.Width;
            float h2 = (float)R1.Height / (float)r1.Height * r2.Height;

            float w2Expect = h2 * 4f / 3f;
            if (w2 > w2Expect)
            {
                h2 = w2 * 3 / 4;
            }
            else
            {
                w2 = w2Expect;
            }

            return new Rectangle((int)x2, (int)y2, (int)w2, (int)h2);
            //return new Rectangle(r2.X+r2.Width,r2.Y,r2.Width,r2.Height);
        }

        private void ArrangeSnap(int index)
        {
            GraphList gl = server.IsContained(this);
            if (gl != null)
            {
                //todo
                //1.自分のグループに関しては[自分以外相対位置表示]
                //2.equalgroupについてはind+=0,directinggroupについてはind+=1として、
                //3.まだ処理していないならば、indのviewをそのグループの一番若い要素にセット。
                //4.再帰的に１を行う

                checkList.Clear();
                SendSnap(gl, index);
                checkList.Clear();
            }
        }
        private void SendSnap(GraphList gl, int ind)
        {
            if (!checkList.Contains(gl))
            {
                checkList.Add(gl);
                SendSnapInGroup(gl, ind);
                foreach (GraphList egl in gl.Equal)
                {
                    SendSnap(egl, ind);
                }
                //if (ind < snap.max)
                //{
                foreach (GraphList egl in gl.Directing)
                {
                    SendSnap(egl, ind + 1);
                }
                //}
                if (ind > 0)
                {
                    foreach (GraphList egl in gl.Directed)
                    {
                        SendSnap(egl, ind - 1);
                    }
                }

            }
        }
        private void SendSnapInGroup(GraphList gl, int ind)
        {
            //グループ内の位置関係にそってSNAPを送る。

            //1.スナップ番号からr=RectangleをＧｅｔ
            //2. SendViewInGroup(gl,r);


            //暫定版
            for (int i = 0; i < gl.Count; i++)
            {
                ClientWithBitmapElement cl = (ClientWithBitmapElement)gl[i];
                cl.Send("/go__snap", ind.ToString());
            }
            
        }
        #endregion
    }

}
